Seattle’s downtown skyline. Credit: Adam Romanowicz

Overview

Seattle is the largest city in Washington State. Situated between the Puget Sound and Lake Washington, it is a largely urban area that is home to nearly 730,000 people. Known for its mild weather and persistent rain, Seattle’s climate is temperate, which until recently has meant that it has avoided extreme temperature events, has abundant fresh water, and is famous for its lush green foliage. However, June of 2021 saw many of the hottest days in Seattle’s history. Moreover, wildfires have become increasingly common in over the past decade. Together, these troubling trends raise questions about how Seattle’s climate is changing and what adaptation measures should be implemented to adapt to these changes. The following report analyzes climate trends in Seattle using daily climate data from the National Climatic Data Center (NCDC). First, average climate variables relating to temperature and precipitation are explored to discover how Seattle’s climate has changed from 1949 until today. Then, frequency and severity of climate extremes are examined.

Setup

Wildfire smoke blankets the city. Credit: Komo 4 News

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)

library(here)
library(tidyverse)
library(janitor)
library(lubridate)
library(tsibble)
library(feasts)
library(gghighlight)
library(plotly)
library(Kendall)
library(kableExtra)
library(broom)

Data

Citation: Climate Data Online (CDO). “National Climatic Data Center (NCDC).” Accessed April 8, 2022. https://www.ncdc.noaa.gov/cdo-web/datasets#GHCND.

seattle <- read_csv(here("data", "seattle.csv")) %>% 
  clean_names() %>% 
  select(-station, -name, -starts_with("wt")) %>% 
  mutate(month = month(date, label = TRUE)) %>% 
  mutate(year = year(date)) %>% 
  drop_na(tmax, tmin, prcp)

Climate Averages

# data wrangling

# converting the dataframe to a time series
seattle_ts <- seattle %>% 
  as_tsibble(key = NULL, index = date) 

# monthly summary statistics
seattle_monthly <- seattle_ts %>% 
  index_by(year_month = ~yearmonth(.)) %>% 
  summarize(mean_daily_max_temp = mean(tmax, na.rm = TRUE), 
            mean_daily_min_temp = mean(tmin, na.rm = TRUE), 
            mean_daily_precip = mean(prcp, na.rm = TRUE), 
            max_temp_monthly = max(tmax, na.rm = TRUE), 
            min_temp_monthly = min(tmin, na.rm = TRUE),
            monthly_precip = sum(prcp, na.rm = TRUE)) %>% 
  mutate(year = year(year_month))

# yearly summary statistics
seattle_yearly <- seattle_ts %>% 
  index_by(year) %>% 
  summarize(mean_daily_max_temp = mean(tmax, na.rm = TRUE), 
            mean_daily_min_temp = mean(tmin, na.rm = TRUE), 
            mean_daily_precip = mean(prcp, na.rm = TRUE),
            total_yearly_precip = sum(prcp)) %>% 
  filter(year != "2022") %>% 
  filter(year != "1948")

# hottest 10 years on record
seattle_hottest <- seattle_yearly %>% 
  slice_max(n = 10, order_by = mean_daily_max_temp)

Temperature

# smooth yearly ggplot
ggplot(data = seattle_yearly) +
  geom_smooth(method = "lm", 
              color = "gray25", 
              aes(x = year, 
                  y = mean_daily_max_temp)) +
  geom_smooth(method = "lm",
              color = "gray25", 
              aes(x = year, 
                  y = mean_daily_min_temp)) +
geom_point(aes(x = year, 
               y = mean_daily_max_temp, 
               color = mean_daily_max_temp), 
           size = 2) +
  geom_point(aes(x = year, 
                 y = mean_daily_min_temp, 
                 color = mean_daily_min_temp), 
             size = 2) +
  scale_color_gradientn(colors = c("deepskyblue4", "firebrick")) +
  theme_minimal(14) +
  theme(legend.position = "none") +
  labs(x = element_blank(), 
       y = "Average Daily Temperature (F)") +
  geom_text(x = 2010, y = 57, label = "Daily Max", color = "firebrick", size = 7) +
  geom_text(x = 2010, y = 43, label = "Daily Min", color = "deepskyblue4", size = 7)

Figure 1 shows Seattle’s average daily maximum temperature (red) and average daily minimum temperature (blue) by year. Trendline was fitted using simple linear regression. Grey shading represents the bounds of a 95% confidence interval.

# seasonplot
seattle_monthly %>% 
  gg_season(y = mean_daily_max_temp) +
  gghighlight(year %in% c("2021", "2020")) +
  theme_minimal(14) +
  labs(x = "Month", 
       y = "Average Daily Temperature (F)")
<<<<<<< HEAD

=======

>>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f

Figure 2 shows seasonal variation in daily maximum temperature across from 1949-2021. Each grey line represents a year since 1949. The past two years are highlighted for reference.

Precipitation

ggplot(data = seattle_yearly, aes(x = year, y = total_yearly_precip)) +
  geom_smooth(method = "lm", color = "deepskyblue4") +
  geom_point() +
  theme_minimal(14) +
  labs(x = element_blank(), y = "Total Annual Precipitation (inches)")

Figure 3 shows Seattle’s annual precipitation (inches) since 1949. Trendline was fitted using simple linear regression. Grey shading represents the bounds of a 95% confidence interval.

seattle_monthly %>% 
  gg_season(y = monthly_precip) +
  gghighlight(year %in% c("2021", "2020")) +
  theme_minimal(14) +
  labs(x = element_blank(), y = "Monthly Precipitation (inches)")
<<<<<<< HEAD

=======

>>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f

Figure 4 shows seasonal variation in monthly precipitation totals. Each grey line represents a year from 1949-2021. The past two years are highlighted for reference.

Statistical Testing

Table 1 displays the results of statistical testing performed on the climate trends data. Slope and P value refer to the results of simple linear regression. M-K significance level refers to the results of Mann-Kendall test performed on each measure.

# linear regressions

# max temp
temp_max_lm <- lm(mean_daily_max_temp ~ year, data = seattle_yearly)
temp_max_tidy <- tidy(temp_max_lm)

# min temp
temp_min_lm <- lm(mean_daily_min_temp ~ year, data = seattle_yearly)
temp_min_tidy <- tidy(temp_min_lm)

# annual precip
yearly_precip_lm <- lm(total_yearly_precip ~ year, data = seattle_yearly)
yearly_precip_tidy <- tidy(yearly_precip_lm)

# monthly precip
monthly_precip_lm <- lm(monthly_precip ~ year, data = seattle_monthly)
monthly_precip_tidy <- tidy(monthly_precip_lm)

lm_tidy_all <- rbind(temp_max_tidy, 
                     temp_min_tidy, 
                     yearly_precip_tidy,
                     monthly_precip_tidy)
lm_tidy_all_coef <- lm_tidy_all %>% 
  filter(term == "year") %>% 
  select(-statistic, -std.error, -term)
         
names_vector <- c("Average Maximum Daily Temperature (F)", "Average Minimum Daily Temperature (F)", "Annual Precipitation (inches)", "Monthly Precipitation (inches)")

lm_tidy_all_clean <- data.frame(names_vector, lm_tidy_all_coef)


# Mann Kendall testing for significance
mk1 <- MannKendall(seattle_yearly$mean_daily_max_temp)
mkt1 <- mk1$sl
mk2 <- MannKendall(seattle_yearly$mean_daily_min_temp)
mkt2 <- mk2$sl
mk3 <- MannKendall(seattle_yearly$total_yearly_precip)
mkt3 <- mk3$sl
mk4 <- MannKendall(seattle_monthly$monthly_precip)
mkt4 <- mk4$sl

mk_vector <- c(mkt1, mkt2, mkt3, mkt4)

stats_table <- data.frame(lm_tidy_all_clean, mk_vector)

kable(stats_table, 
      col.names = c("Measurement", "Slope", "P value", "M-K Significance Level"), 
      digits = c(0, 4, 4, 4)) %>% 
  kable_minimal(full_width = FALSE)
Measurement Slope P value M-K Significance Level
Average Maximum Daily Temperature (F) 0.0409 0.0000 0.0000
Average Minimum Daily Temperature (F) 0.0533 0.0000 0.0000
Annual Precipitation (inches) 0.0071 0.8471 0.8977
Monthly Precipitation (inches) 0.0005 0.9087 0.9896

Conclusions

  • Average daily maximum temperature is increasing on average by 0.04 degrees F per year, and this trend was determined to be significant (p < 0.001).
  • Average daily minimum temperature is also increasing, on average by 0.05 degrees F, and this trend was determined to be significant (p < 0.001).
  • Thus temperatures both maximum and minimum daily average temperatures are increasing in the Seattle area.
  • There is not a significant trend in either monthly and annual precipitation, indicating that precipitation levels are not changing over time in Seattle.

\[\\[.5in]\]

Climate Extremes

# Data wrangling

# Temperature

# hottest day of the year 1949-2021
seattle_hottest_days <- seattle_ts %>% 
  index_by(year) %>% 
  summarize(hottest_day = max(tmax)) %>% 
  filter(year != "2022") %>% 
  filter(year != "1948")

# days above 85 and days below freezing 1949-2021
seattle_hot_days <- seattle_ts %>% 
  index_by(year) %>% 
  summarize(days_above_85 = sum(ifelse(tmax >= 90, 1, 0))) %>% 
  filter(year != "2022") %>% 
  filter(year != "1948")

# days below freezing 1949-2021
seattle_below_freezing <- seattle_ts %>% 
  index_by(year) %>% 
  summarize(days_below_freezing = sum(ifelse(tmin <= 32, 1, 0))) %>% 
  filter(year != "2022") %>% 
  filter(year != "1948")
# Data wrangling

# precipitation extremes

# days with over an inch of precipitation
seattle_wet_days <- seattle_ts %>% 
  index_by(year) %>% 
  summarize(wet_days = sum(ifelse(prcp >= 1, 1, 0))) %>% 
  filter(year != "2022") %>% 
  filter(year != "1948")

# days with no precipitation
seattle_dry_days <- seattle_ts %>% 
  index_by(year) %>% 
  summarize(dry_days = sum(ifelse(prcp < 0.1, 1, 0))) %>% 
  filter(year != "2022") %>% 
  filter(year != "1948")

Temperature Extremes

<<<<<<< HEAD
# plotting hottest day of the year 1949-2021
ggplot(data = seattle_hottest_days) +
  geom_smooth(method = "lm", 
              color = "gray25", 
              aes(x = year, 
                  y = hottest_day)) +
geom_point(aes(x = year, 
               y = hottest_day, 
               color = hottest_day), 
           size = 2) +
  scale_color_gradientn(colors = c("firebrick")) +
  theme_minimal(14) +
  theme(legend.position = "none") +
  labs(x = element_blank(), 
       y = "Hottest Day of the Year (F)")

======= >>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f
# plotting number of days over 85 

ggplot(data = seattle_hot_days) +
  geom_smooth(method = "lm", 
              color = "gray25", 
              aes(x = year, 
                  y = days_above_85)) +
geom_point(aes(x = year, 
               y = days_above_85, 
               color = days_above_85), 
           size = 2) +
  scale_color_gradientn(colors = c("firebrick")) +
  theme_minimal(14) +
  theme(legend.position = "none") +
  labs(x = element_blank(), 
       y = "Number of Days Over 85 Degrees (F)")
<<<<<<< HEAD

=======

Figure 5 displays the number of days per year in Seattle where the daily maximum temperature exceeded 85 degrees Fahrenehit. The trendline was fitted using simple linear regression. Grey shading represents the bounds of a 95% confidence interval.

>>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f
# plotting number of days below freezing

ggplot(data = seattle_below_freezing) +
  geom_smooth(method = "lm", 
              color = "gray25", 
              aes(x = year, 
                  y = days_below_freezing)) +
geom_point(aes(x = year, 
               y = days_below_freezing, 
               color = days_below_freezing), 
           size = 2) +
  scale_color_gradientn(colors = c("deepskyblue4")) +
  theme_minimal(14) +
  theme(legend.position = "none") +
  labs(x = element_blank(), 
       y = "Number of Nights Below Freezing")
<<<<<<< HEAD

=======

<<<<<<< HEAD Figure 6 shows the number of days per year in Seattle where the minimum daily temperature dropped below freezing. The trendline was fitted using simple linear regression. Grey shading represents the bounds of a 95% confidence interval. ======= >>>>>>> 926dd6cb0f7846f2b5689e810f06b56de0d95963

>>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f

Precipitation Extremes

ggplot(data = seattle_wet_days) +
  geom_smooth(method = "lm", 
              color = "gray25", 
              aes(x = year, 
                  y = wet_days)) +
  geom_point(aes(x = year, 
               y = wet_days, 
               color = wet_days), 
           size = 2) +
  theme_minimal(14) +
  theme(legend.position = "none") +
  labs(x = element_blank(), 
       y = "Number of Days with over 1 Inch of Precipitation")
<<<<<<< HEAD

=======

Figure 7 shows the number of wet days in Seattle per year, determined by days with at least 1 inch of precipitation. The trendline was fitted using simple linear regression. Grey shading represents the bounds of a 95% confidence interval.

>>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f
ggplot(data = seattle_dry_days) +
  geom_smooth(method = "lm", 
              color = "gray25", 
              aes(x = year, 
                  y = dry_days)) +
  geom_point(aes(x = year, 
               y = dry_days, 
               color = dry_days), 
           size = 2) +
  theme_minimal(14) +
  theme(legend.position = "none") +
  labs(x = element_blank(), 
       y = "Number of Days with No Precipitation")
<<<<<<< HEAD

Refer to your answer for part 1 when choosing your metrics; why are these metrics likely to be relevant, given the climate change impacts you expect at this location?

=======

<<<<<<< HEAD Figure 8 shows the number of dry days in Seattle per year, respresented by days where there was no measured precipitation. The trendline was fitted using simple linear regression. Grey shading represents the bounds of a 95% confidence interval.

Refer to your answer for part 1 when choosing your metrics; why are these metrics likely to be relevant, given the climate change impacts you expect at this location? =======

Refer to your answer for part 1 when choosing your metrics; why are these metrics likely to be relevant, given the climate change impacts you expect at this location? >>>>>>> 926dd6cb0f7846f2b5689e810f06b56de0d95963

>>>>>>> dd256ab879007ea8d2d67b5d71e1edfb8ca1490f

T, rank-sum, or other statistical tests

Discussion